﻿using System;
using System.Drawing;
using CanvasControl.Model;

namespace GraphDrawing.Graph.Model
{
    /// <summary>
    /// Представление слота блока.
    /// </summary>
    public class SlotModel
    {
        /// <summary>
        /// Стратегия поведения слота.
        /// </summary>
        readonly ISlotStrategy strategy;

        /// <summary>
        /// Модель фигуры слота.
        /// </summary>
        readonly IJointedShape shapeModel;

        /// <summary>
        /// Состояние текущего слота.
        /// </summary>
        public SlotState state;

        /// <summary>
        /// Ссылка на блок, которому принадлежит слот.
        /// </summary>
        public BlockModel Parent { get; set; }

        /// <summary>
        /// Расположение слота в блоке.
        /// Представляет собой координаты левого верхнего угла прямоугольника, в который вписана фигура, представляющая слот, в системе координат родительского блока.
        /// </summary>
        public Point Position { get; set; }

        /// <summary>
        /// Создаёт экземпляр слота.
        /// </summary>
        /// <param name="strategy">Стратегия поведения слота.</param>
        /// <param name="shapeModel">Модель фигуры.</param>
        public SlotModel(ISlotStrategy strategy, IJointedShape shapeModel)
        {
            this.strategy = strategy;
            this.shapeModel = shapeModel;
        }

        /// <summary>
        /// Проверяет, располагается ли точка <paramref name="point"/> внутри области слота.
        /// </summary>
        /// <param name="point">Координаты точки в области представления.</param>
        /// <returns>Возвращает true, если точка лежит внутри фигуры, которой представлен слот, иначе false.</returns>
        public bool HitTest(Point point)
        {
//            var parentPos = Parent.Position;
//            var translatedPoint = point.Translate(parentPos).Translate(Position);
//            return shapeModel.HitArea.Contains(translatedPoint);
            return false;
        }

        /// <summary>
        /// Возвращает границы области, в которой вписана фигура, которой представлен слот.
        /// </summary>
        public Rectangle GetBounds()
        {
            return new Rectangle();
//            return new Rectangle(Parent.position.Move(Position), shapeModel.HitArea.Size);
        }

        /// <summary>
        /// Определяет, может ли соединение от слота <paramref name="other"/> быть принято в текущий слот.
        /// </summary>
        /// <param name="other">Слот, из которого создаётся соединение.</param>
        public bool CanAccept(SlotModel other)
        {
            return (Parent != other.Parent || strategy.CanAcceptFromSameBlock) && strategy.CanAccept(other.strategy);
        }

        /// <summary>
        /// Пытается соединить текущий слот с другим.
        /// </summary>
        /// <param name="otherSlotModel">Слот-источник.</param>
        /// <returns></returns>
        public void Connect(SlotModel otherSlotModel)
        {
            if (!CanAccept(otherSlotModel))
                throw new InvalidOperationException();
            strategy.Accept(otherSlotModel.strategy);
        }
    }

    /// <summary>
    /// Стратегия поведения слота.
    /// </summary>
    public interface ISlotStrategy
    {
        /// <summary>
        /// Флаг, равный true, если слот может принимать слоты из одного блока, иначе false.
        /// </summary>
        bool CanAcceptFromSameBlock { get; }

        /// <summary>
        /// Определяет, может ли слот принимать данные определённого типа.
        /// </summary>
        /// <param name="other">Стратегия слота, с которым проверяется возможность соединения.</param>
        /// <returns>Возвращает true, если слот может быть соединён с другим слотом, иначе false.</returns>
        bool CanAccept(ISlotStrategy other);

        /// <summary>
        /// Принимает значение, предоставляемое другим слотом.
        /// </summary>
        /// <param name="other">Предоставляемое значение.</param>
        void Accept(ISlotStrategy other);
    }

    /// <summary>
    /// Состояние слота.
    /// TODO: для расширяемости можно использовать ISlotState и визитор на нём для реализации различных состояний.
    /// </summary>
    public enum SlotState
    {
        /// <summary>
        /// Неактивное состояние.
        /// </summary>
        DEFAULT,

        /// <summary>
        /// 
        /// </summary>
        HOVERED,

        /// <summary>
        /// Слот выделен.
        /// </summary>
        FOCUSED,

        /// <summary>
        /// Слот доступен для создания соединения.
        /// </summary>
        COMPATIBLE,

        /// <summary>
        /// Слот недоступен для создания соединения.
        /// </summary>
        INCOMPATIBLE
    }
}
